<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>使用class关键字的类</title>
</head>

<body>
  <script>
    class Range {
      constructor(from, to) {
        this.from = from;
        this.to = to
      }
      includes(x) { return this.from <= x && x <= this.to };
      // 通过计算的名字定义的生成器方法
      *[Symbol.iterator]() {
        for (let x = Math.ceil(this.from); x <= this.to; x++)yield x;
      };
      toString() { return "(" + this.from + "..." + this.to + ")" }

      // 静态方法
      static integerRangePattern = /^\((\d+)\.\.\.(\d+)\)$/;
      static parse(s) {
        let matches = s.match(Range.integerRangePattern)
        if (!matches) {
          throw new TypeError(`Cannot parse Range from "${s}"`)
        }
        return new Range(parseInt(matches[1]), parseInt(matches[2]))
      }
    }

    let r = new Range(1, 3)
    let r1 = Range.parse('(1...5)')
    console.log(r);
    console.log(r1);
    console.log(r.includes(3));
    console.log(r.toString());
    console.log([...r]);
    console.log(r.constructor);

    // 与函数定义类似，类声明也有语句和表达式两种形式
    console.log(square(3));
    function square(x) { return x * x }
    // 也可以这样写
    let Square = class { constructor(x) { this.area = x * x } }
    console.log(new Square(3).area);
    // class声明体中的所有代码默认处于严格模式，类声明和函数声明不同，类声明不会提升

    // 9.3.1静态方法：静态方法是作为构造函数而非原型对象的属性定义的

    // 9.3.4示例：复数类
    /*
    这个复数类的示例代表复数
    复数是一个实数和一个虚数之和
    而虚数i是-1的平方根
    */
    class Complex {
      // 在这种类字段声明标准化之后，我们可以像下面这样，声明私有字段来保存复数的
      // 实数和虚数部分：
      // #r=0；#i=0

      // 下面构造函数定义了他需要在每个实例上
      // 创建的实例属性r和i。这两个字段保存
      // 复数的实数和虚数部分，即对象的状态
      constructor(real, imaginary) {
        this.r = real;
        this.i = imaginary
      }
      // 这里是两个实例方法，用于做复数的加法和乘法。如果c和d是这个类两个实例，则可以写c.plus(d)或d.times(c)
      plus(that) {
        return new Complex(this.r + that.r, this.i + that.i)
      }
      times(that) {
        return new Complex(this.r * that.r - this.i * that.i, this.r * that.i + this.i * that.r)
      }

      // 这里是两个复数计算方法的静态版本。这样可以写Complex.sum（c,d）和Complex.product(c,d)
      static sum(c, d) { return c.plus(d) }
      static product(c, d) { return c.times(d) }

      // 这些也是实例方法，但是使用获取函数定义的，因此可以像使用字段一样使用它们。如果我们使用的是私有字段this.#r和this.#i，那这里的获取方法就有用了
      get real() { return this.r }
      get imaginary() { return this.i }
      get magnitude() { return Math.hypot(this.r, this.i) }

      // 每个类都应该有一个toString（）方法
      toString() { return `{${this.r},${this.i}}` }

      // 这个方法可以用来测试类的两个实例是否表示相同的值
      equals(that) {
        return that instanceof Complex && this.r == that.r && this.i == that.i
      }
      // 如果类体支持静态字段，那我们就可以像下面这样定义一个常量Complex.ZERO:static ZERO=new Complex(0,0)
    }
    // 下面定义了几个保存预定义复数的类字段
    Complex.ZERO = new Complex(0, 0)
    Complex.ONE = new Complex(1, 0)
    Complex.I = new Complex(0, 1)
    console.log(Complex.ZERO);
    console.log(Complex.ONE);
    console.log(Complex.I);

    let c = new Complex(2, 3)//通过构造函数创建一个新对象
    console.log(c);
    let d = new Complex(c.i, c.r)//使用实例字段
    console.log(d);
    console.log(c.plus(d).toString());//{5,5}；使用实例方法
    console.log(c.magnitude);//Math.hypot(2, 3)；使用获取函数
    console.log(Complex.product(c, d));//使用静态方法（类方法）
    console.log(Complex.ZERO.toString());//{0,0}使用静态属性
  </script>
</body>

</html>